package com.zw.rpc.server.config;

import com.zw.rpc.Request;
import com.zw.rpc.Response;
import com.zw.rpc.codec.Decoder;
import com.zw.rpc.codec.Encoder;
import com.zw.rpc.common.utils.ReflectionUtils;
import com.zw.rpc.transport.TransportServer;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;

import java.io.IOException;

/**
 * @author zw
 */
@Data
@Slf4j
public class RpcServer {

    private RpcServerConfig config;

    private TransportServer net;

    private Encoder encoder;

    private Decoder decoder;

    private ServiceManager serviceManager;

    private ServiceInvoker invoker;

    public RpcServer(RpcServerConfig config) {
        this.config = config;
        this.net = ReflectionUtils.newInstance(config.getTransportClass());
        this.net.init(config.getPort(), (received, toResp) -> {
            Response response = new Response();
            try {
                byte[] bytes = IOUtils.readFully(received, received.available());
                Request request = this.decoder.decode(bytes, Request.class);
                ServiceInstance serviceInstance = serviceManager.lookup(request);
                Object invoke = invoker.invoke(serviceInstance, request);
                response.setData(invoke);

            } catch (Exception e) {
                log.warn(e.getMessage(), e);
                response.setCode(1);
                response.setMessage("RpcServer get error:" + e.getClass().getName() + " : " +
                        e.getMessage());
            } finally {
                byte[] encode = this.encoder.encode(response);
                try {
                    toResp.write(encode);
                    log.info("RpcServer response: {}",response);
                } catch (IOException e) {
                    log.warn(e.getMessage(), e);
                }
            }


        });
        this.encoder = ReflectionUtils.newInstance(config.getEncoderClass());
        this.decoder = ReflectionUtils.newInstance(config.getDecoderClass());
        this.serviceManager = new ServiceManager();
        this.invoker = new ServiceInvoker();
    }

    public void start() {
        this.net.start();
    }

    public void stop() {
        this.net.stop();
    }

    public <T> void register(Class<T> interfaceInstance, T bean) {
        serviceManager.register(interfaceInstance, bean);
    }

}
